Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.99/160: Рейтинг темы: голосов - 160, средняя оценка - 4.99
 Аватар для Alexey104
4 / 4 / 0
Регистрация: 26.10.2014
Сообщений: 105

Зачем нужны дружественные функции

19.07.2015, 20:45. Показов 30856. Ответов 33
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте!
Начал изучать "друзей" в С++, но в моём странном учебнике данная тема описывается буквально в 2-х абзацах. В одном абзаце даётся определение дружественных классов, в другом - дружественных функций. Далее без всяких примеров использования "друзей" начинается новая тема. Решил я почитать статьи в интернете по этой теме, начал с дружественных функций. Перечитав множество статей, я понял, что из себя представляют friend-функции, как они работают и объявляются, но я нигде не нашёл примеров, демонстрирующих хоть какую-то пользу от них. Все примеры, если их максимально упростить, сводятся примерно к следующему:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
 
using namespace std;
 
class SomeClass
{
  private:
    int itsVal;
    friend void SetVal(int value, SomeClass &theClass) {theClass.itsVal = value;}
    friend void GetVal(SomeClass &theClass) {cout << theClass.itsVal << "\n";}
  public:
    SomeClass(): itsVal(96) {}
    void Show() const {cout << itsVal << "\n";}
};
 
 
int main()
{
  SomeClass theClass;
  theClass.Show();
  SetVal(12, theClass);
  GetVal(theClass);
  return 0;
}
Из данного кода видно, что friend-функция, хоть и не является членом класса, тем не менее, имеет полный доступ ко всем её членам и методам, включая те, что объявлены в области 'private' или 'protected'. Но тот же код можно было выполнить, используя обычный метод класса:

C++
1
void SomeClass::SetVal(int value) {itsVal = value;}
Такой пример не демонстрирует ни одного преимущества friend-функции перед обычным методом класса. Но не просто же так friend-функции существуют в языке!

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

В то же время автор этой же статьи утверждает, что, поскольку friend-функция не принадлежит никакому классу, то и указателя 'this' она не имеет, а, следовательно, в качестве одного из параметров такая функция должна обязательно получать ссылку или указатель на конкретный объект, с которым ей придётся работать.

Попробуем осуществить задумку автора:
Создадим десять классов и два глобальных метода 'Set' и 'Get', которые сделаем друзьями для всех десяти классов. Автор уверяет, что таким образом мы сэкономим кучу времени и строк. Но для того, чтобы методы были друзьями для всех классов, они должны в качестве параметров получить ссылки(или указатели) на объекты всех 10-ти классов, чтобы уметь с ними работать. А в теле этих методов должны быть определены действия для каждого из объектов. Получается, в коде мы нисколько не сэкономим, да и методы наши смогут обрабатывать лишь 10 объектов одновременно(хотя, возможно, в некоторых случаях это полезно?).
С другой стороны, если я хочу иметь возможность вызывать friend-функцию лишь для одного конкретного объекта класса, я могу переопределить эту функцию в теле этого класса. Но, опять же, на коде я нисколько не сэкономлю, да и смысла нет использовать в данном случае friend-функцию.
В общем, задумку автора я так и не понял.

Может кто-нибудь привести адекватные простенькие примеры, демонстрирующие реальную пользу friend-функций?
Спасибо!
2
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
19.07.2015, 20:45
Ответы с готовыми решениями:

приведите пожалуйста реальный прмиер: для чего нужны дружественные функции?
приведите пожалуйста реальный прмиер: для чего нужны дружественные функции?

Функции.Зачем нужны void функции?
Здравствуйте люди.Я учусь по книге C++ для чайников.Там объясняют многое про функции.Одного не могу понять.Зачем нужны void функции с...

Зачем биты нужны это меньше байтов но int 32 бита но я не допер зачем это нужно это 4 байта то есть int не может больше 4 байт весить?
Вот еще один вопрос зачем биты нужны это меньше байтов но int 32 бита но я не допер зачем это нужно это 4 байта то есть int не может...

33
807 / 534 / 158
Регистрация: 27.01.2015
Сообщений: 3,017
Записей в блоге: 1
20.07.2015, 00:35
Студворк — интернет-сервис помощи студентам
Alexey104, после прочтения советовал бы вникнуть в глубокие детали синтаксиса языка, чтобы например понимать разницу между
C++
1
int x = 1;
и
C++
1
int x = 1F;
ну или разницу между этим
C++
1
const short* x;
и
C++
1
short* const x;
Добавлено через 1 минуту
Ну скомпилировалось и ладно, бог с ним =)
0
 Аватар для Alexey104
4 / 4 / 0
Регистрация: 26.10.2014
Сообщений: 105
20.07.2015, 00:44  [ТС]
Ferrari F1,
Всегда приятно получить лестный отзыв от того, кто в чём-то лучше тебя!)

Добавлено через 2 минуты
Цитата Сообщение от Ferrari F1 Посмотреть сообщение
т.е. ты седня утром взялся за изучение Сипипи? Аа ну тогда у тебя очень неплохие успехи, если ты за день все темы поднял с нуля))
Это я к этому.

Добавлено через 5 минут
Цитата Сообщение от Ferrari F1 Посмотреть сообщение
int x = 1F;
Это я не понял.
Цитата Сообщение от Ferrari F1 Посмотреть сообщение
const short* x;
Указатель на константу.
Цитата Сообщение от Ferrari F1 Посмотреть сообщение
short* const x;
Константный указатель.
0
 Аватар для агерон
447 / 300 / 65
Регистрация: 12.10.2009
Сообщений: 1,162
20.07.2015, 01:00
Eraston, для вашего случая подойдет типизированая функция на вход которой подаются наследники интерфейса IGeometry реализурующие доступ к соответствующим полям для расчетов. Все просто и со вкусом
Повторюсь - приватные члены на то и приватные чтобы в них кто попало не лазил, хотите доступ к приватному члену класса не поленитесь реализуйте функциональность get/set через шаблонный класс

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
template <class proptype> class property:virtual public Object
        {
            protected:
                boost::shared_ptr<proptype> _value;
                
            public:
 
                property():_value((proptype*)(new proptype()))
                {}
 
                operator proptype()
                {
                    return *_value;
                }
 
                void operator =(proptype data)
                {
                    *_value = data;
                }
 
                void operator =(boost::shared_ptr<proptype> data)
                {
                    *_value = *data;                    
                }
 
                proptype * operator->() const
                {
                    return _value.get();
                }
 
                string ToString() 
                {
                    return boost::lexical_cast<string>(*_value);
                }
 
                ~property()
                {
                    _value.reset();
                }
        };
1
 Аватар для Alexey104
4 / 4 / 0
Регистрация: 26.10.2014
Сообщений: 105
20.07.2015, 01:19  [ТС]
Цитата Сообщение от Eraston Посмотреть сообщение
если у тебя будет много классов со схожими параметрами (например, материально-пространственные объекты с одноименными переменными типа: x, y, z), для каждого из которых тебе надо прописать функцию со сложными вычислениями (какое-то хитрое перемещение), то в каждом классе придется описывать метод. Но, вместо, можно объявить friend-функции в этих классах, описать её 1 раз, и с её помощью можно точно так же проводить вычисления для любого экземпляра любого класса с его приватными членами.
Это именно то, о чём я спрашивал в начале темы. Задумка мне ясна, но как это реализовать? Допустим, у нас имеется 2 класса, оба из которых имеют целочисленные переменные члены. Я хочу такую функцию, которая бы меняла значения этих членов. Вы говорите, что если сделать эту функцию дружественной по отношению к обоим классам, то определить её достаточно единожды, после чего функцию можно будет использовать для объекта любого класса. Попробуем:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
 
using namespace std;
 
class B; 
class A
{
  private:
    int itsVal;
    friend void SetVal(A &, B &, int);
  public:
    A(): itsVal(12) {}
    void Show() const {cout << itsVal << "\n";}
};
 
class B
{
private:
    int itsVal;
    friend void SetVal(A &, B &, int);
  public:
    B(): itsVal(35) {}
    void Show() const {cout << itsVal << "\n";}
};
 
void SetVal(A &a, B &b, int value)
{
  a.itsVal = value;
  b.itsVal = value;
}
 
int main()
{
  A a;
  B b;
  a.Show();
  b.Show();
  SetVal(a, b, 99);
  a.Show();
  b.Show();
  return 0;
}
Да, получилось, функцию 'SetVal' мы определили лишь единожды, в классах мы лишь прописали её прототип, в итоге, функция работает как надо, вот только с двумя объектами одновременно, ведь в связи с отсутствием у функции указателя 'this', мы должны явно указать ей, с какими объектами работать. Если мы хотим, чтобы функция работала с двумя объектами, мы должны передать ей эти объекты в качестве параметров(точнее, ссылки или указатели на эти объекты) и прописать в теле этой функции, какие действия над полученными объектами она должна произвести. Получается, вызов такой функции возможен лишь для всех объектов, по отношению к которым она является дружественной, одновременно. А если я хочу вызвать эту функцию для объекта 'a', не трогая объект 'b'? Как это сделать?
0
 Аватар для iRomul
163 / 104 / 14
Регистрация: 17.10.2012
Сообщений: 488
20.07.2015, 01:34
С помощью механизма дружественных классов\функций и препроцессора, можно реализовать паблик морозова, т.е. прописать в качестве друга свой класс\функцию, тем самым получив доступ к приватным полям.
C++
1
2
3
4
5
6
7
#define private friend class Descendant; private // приписываем friend class Descendant во все классы
#include <Ancestor.hpp>
#undef private
...
 
Descendant *h = new Descendant();
int x = h->m_value; // m_value в прошлой жизни - private из Ancestor
0
 Аватар для Eraston
60 / 11 / 4
Регистрация: 09.09.2014
Сообщений: 182
20.07.2015, 04:11
агерон, предлагаете через get мозолить в треде приваты класса каждые 0,1 ms?

Добавлено через 2 минуты
Просто у меня тогда возникает вопрос. По быстродействию. Friend-функция будет быстрее? Или .Get не будет медленнее? Или вынести переменные в public, написать тред-функцию работающую с ними и на этом успокоиться?

Добавлено через 1 час 16 минут
Alexey104, Дружественные функции. Порыскал в интернете, есть еще идея использования френда для вычисления значений двух разных классов (например, перемножение одномерного и двумерного массивов).
Касательно вашего поста:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class a{
public:
    a():a1(2){};~a(){};
    int s() { d = 45;};
private:
    template <typename T> friend int get(T&);
    int a1;
    double d;
};
 
class b{
public:
    b():a1(6){};~b(){};
    int s() {};//{ a1 = 35; return a1; }
    void f(){ int p = 4 + 6; }
private:
    template <typename T> friend int get(T&);
    float b6;
    int a1;
};
 
template <typename T> int get(T&u){
    return u.a1;
}
 
int main(int argc, _TCHAR* argv[])
{
    a k1;b k2;
    do{
        printf( "> " );
        memset( cmd, 0, L_SERVER_CMD );
        scanf( "%s", cmd, sizeof(cmd) );
        if( !strcmp( cmd, "t" ) ){
            printf("\n %d %d\n",get(k1),get(k2));
        }
    }while( strcmp( cmd, "0" ) && strcmp( cmd, "exit" ) );
    return 0;
}
Вывод: 2 6

В общем-то, по сути, вместо описания метода в каждом классе, получим n-раз перегруженую функцию. Но главное-то, ради чего морока - строчек кода меньше.
1
 Аватар для Alexey104
4 / 4 / 0
Регистрация: 26.10.2014
Сообщений: 105
20.07.2015, 05:11  [ТС]
Eraston,
Большое спасибо за потраченное на меня время, но, к сожалению, я ещё не дорос до чтения таких кодов. Я не понимаю, что значит
C++
1
template <typename T> friend int get(T&);
или
C++
1
2
memset( cmd, 0, L_SERVER_CMD );
        scanf( "%s", cmd, sizeof(cmd) );
В любом случае из этого поста я понял, что френд-функции(и классы) могут быть достаточно полезны, при том, что инкапсуляцию они вовсе не нарушают, а, скорее, расширяют(я ведь сам решаю, кому открывать доступ к членам класса, а кому - нет). Думаю, с опытом придёт более глубокое понимание многих вещей.
0
 Аватар для агерон
447 / 300 / 65
Регистрация: 12.10.2009
Сообщений: 1,162
20.07.2015, 06:14
Eraston, самое главное что данный подход удобен в РАЗРАБОТКЕ облегчает жизнь в районе get/set :-D быстродействие? забудь такое, время программиста важнее и дороже, ты надеюсь помнишь правило Парето по отношению к коду? - "80% времени жизненного цикла программы работает 20% кода" вот когда я упрусь в то что мой код входит в эти 20% и тормозит к придачу вот тогда может я перепишу данный код инлайном или фасмом а пока.... пока я предпочитаю потерю скорости кода за счет удобства разработки т. к. программирование это конструктор из мелких кирпичиков благодаря которому можно строить ажурные конструкции и кстати самое страшное не то что ты потеряешь 0.0001 на преобразовании в каждой операции а то что ты вовремя не сдашь СВОЮ часть проекта т. к. из-за этого ты подводишь команду в которой ты работаешь а соответственно и себя. у современных программистов нет такой роскоши как вылизывание каждой строчки т. к. за это не заплатят, программист должен сразу писать РАБОТАЮЩИЙ код, а оптимизации - оптимизации придут потом если деньги на это выделят, вот и приходиться делать так чтобы тебе было комфортно писать код изначально принося в жертву быстродействие, т. к. железо стоит дешевле чем время программиста
0
Модератор
Эксперт CЭксперт С++
 Аватар для sourcerer
5288 / 2376 / 342
Регистрация: 20.02.2013
Сообщений: 5,773
Записей в блоге: 20
20.07.2015, 08:21
Цитата Сообщение от Alexey104 Посмотреть сообщение
Учебник называется смешно - "C++ за 24 часа".
Что и требовалось доказать.
0
20.07.2015, 09:46

Не по теме:

агерон, не поэтому ли к играм выходят бесконечные патчи? :)

0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
20.07.2015, 10:19
Цитата Сообщение от Alexey104 Посмотреть сообщение
но я нигде не нашёл примеров, демонстрирующих хоть какую-то пользу от них.
ну если вкратце, френды это:
"ты, ты, и ты - имеете права доступа к моим личным данным. а остальные идут нафиг".

вообще, тема холиварная, и было сломанно не мало копий об неё.

если привести общественное мнение к общему знаменателю,
то смысл звучит так:

"френды - это зло, но зло необходимое".

мне очень понравилось, как об этом высказалася Макконелл в своей книге "Идеальный код":

"большое количество френдов в проекте - признак ущербной архитектуры".

обратите внимание, Макконелл не говорит, что фреды - это плохо.
он говорит, что плохо, когда их много.

почему?

потому что случаев их грамотного использования не так уж и много.
некоторые примеры такого использования
вам уже продемонстировал господин DrOffset

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

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


что касается самих грамотных случаев полезного использования:
вам уже продемонстрировали некоторые из этих случаев.

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

но как только вы с этим столкнетесь,
вы сами вспомните про волшебные френды,
и все поймете.

я думаю правильный ответ здесь может звучать так:
вам нужно набить шишки.
3
 Аватар для Eraston
60 / 11 / 4
Регистрация: 09.09.2014
Сообщений: 182
20.07.2015, 12:16
nmcf, именно поэтому.
агерон, я пока учусь, а в учении все надо как по науке, а когда научишься, уже можно стряпать как угодно ) я так кстати не понял, про какой igeometry вы говорили.
0
Игогошка!
 Аватар для ct0r
1801 / 708 / 44
Регистрация: 19.08.2012
Сообщений: 1,367
20.07.2015, 13:03
Alexey104, DrOffset, еще вариант использования, в CRTP: http://accu.org/index.php/journals/296

Цитата Сообщение от агерон Посмотреть сообщение
когда я упрусь в то что мой код входит в эти 20% и тормозит к придачу вот тогда может я перепишу данный код инлайном или фасмом
Вот прям так сразу? Надо посмотреть на архитектуру, на способ решения задачи, на алгоритмы и структуры данных, может быть на cache-locality и в этом роде, но уж никак не использовать сразу фасм

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

Цитата Сообщение от агерон Посмотреть сообщение
у современных программистов нет такой роскоши как вылизывание каждой строчки т. к. за это не заплатят, программист должен сразу писать РАБОТАЮЩИЙ код
Тем не менее работающий код не должен обладать всеми признаками древнего дерьма мамонта. Он должен быть качественным, так или иначе. А за качество платят. Той же самой стоимостью тестирования и сопровождения.

Цитата Сообщение от агерон Посмотреть сообщение
комфортно писать код изначально принося в жертву быстродействие, т. к. железо стоит дешевле чем время программиста
И еще раз. Нужно рассматривать каждую ситуацию отдельно. Нужно идти на компромиссы. Нужно знать на какие. Бывают задачи, где одна секунда простоя системы дороже зарплаты программиста за месяц.
1
923 / 639 / 198
Регистрация: 08.09.2013
Сообщений: 1,693
20.07.2015, 16:07
Цитата Сообщение от Alexey104 Посмотреть сообщение
реальную пользу friend-функций?
<На правах сарказма>
Дружественные функции ясно демонстрируют идеологию Си++ - быть для всех всем.
Один из принципов этой идеологии - должна быть заложена бай-дизайн возможность нарущения любого стиля написания кода.
С одной стороны фрэнды явно нарушают инкапсуляцию в её ООП-шном понимании. Но это не должно нас смущать, поскольку сам ООП пришит к Плюсам как пятая нога к собаке.
С другой стороны, с позиций Си-лайк программистов, друзья излишни, так как сами приватные члены положительно не решают никакой проблемы. С не меньшим профитом члены класса можно не упоминать в документации, а в исходниках обозначить как недокументированные и могущие измениться или исчезнуть в следующей версии. Но Плюсы на то и Плюсы, чтобы мы сами создавали себе проблемы, которые потом героически решали, подпирая и так раздутый язык всё новыми костылями.
</>
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
20.07.2015, 16:07

Зачем нужны операторы << и >>
В книжке Дейтлов есть код http://pic.ipicture.ru/uploads/091222/thumbs/q1TZw4n1JQ.jpg Вопрос в том, что там где написано, что числа...

Зачем нужны указатели?
Интересует вопрос, зачем нужны указатели? Например почему лучше нужно объявлять переменные как указатели, почему как обычно нельзя? ...

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

Зачем нужны скобочки?
Объясните пожалуйста зачем надо ставить в коде так много скобок? if(((a+b)*(a-b)))

Зачем нужны макросы?
Зачем нужны макросы?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
34
Ответ Создать тему
Новые блоги и статьи
GitDOS: Когда я решил скрестить MS-DOS, GitHub и Claude
MaGz GoLd 08.07.2026
Привет, Киберфорум! Хочу поделиться своим проектом, который, надеюсь, вас заинтересует. GitDOS — это веб-приложение, которое позволяет запускать DOS в браузере с тремя крутыми фичами: диски живут в. . .
Учебный мини "цифровой двойник" = (Сервер = Java + Flask_питон )+(3D = Гугловскаая 4T nvideo + google colab) +( WMS = Odoo на Docker)
anaschu 07.07.2026
Увидел в требованиях к вакансии создателя имитационных моделей связку (REst + Api + WMS + Anylogic + питон + 3D Nvideo), и решил посмотреть, что это такое. Из этого всего я работал более менее. . .
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
Doom для терминала без стрельбы и монстров. 3D Raycasting на ascii.
dcc0 05.07.2026
Попросил нейронную сеть deepai. org написать рейкастинг 3D с библиотекой ncurses для Linux. Чтобы можно было ходить на стрелочки. Чтобы стены были отрисованы символами. Справилась. Первый вариант. . .
Установка статуса документа по условию
Maks 05.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "НарядПутевка" разработанного в КА2. Задача: в табличной части "Материалы" документа при записи автоматически устанавливать статус. . .
Сезонность и суточность закисления почв
anaschu 04.07.2026
200 часов это все равно моловато. Есть ситуации, но нестандартные, когда смена происходит за 5 лет. Но обычно это 50 лет и более. Наверное, закисление почвы происходит сезонно в средней. . .
В чем ценность человеческого опыта в глобальном смысле?
kumehtar 03.07.2026
Возможно, ценность человека не в том, что он однажды достигает мудрости, а в том, что он становится носителем карты пути. Он знает не только истину, но и последовательность внутренних изменений,. . .
интеграция AnyLogic с самописным REST API и переход на Odoo
anaschu 03.07.2026
Успешная интеграция AnyLogic с самописным REST API и переход на промышленную Odoo WMS Сегодня проделал огромный путь от простой симуляции физических процессов до построения полноценной. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru