В этой статье мы расскажем о следующем:
- Что такое дружественные функции и, главное, зачем они нужны.
- Чем отличаются обычные методы класса от дружественных функций класса.
- Как объявлять и определять дружественные функции.
- Как вызывать дружественные функции из главной
main()
функции .
Самым важным, но и, скорее всего, самым непонятным для вас сейчас станет определение дружественной функции. Дружественная функция — это функция, которая не является членом класса, но имеет доступ к членам класса, объявленным в полях private
или protected
. Долго не вникайте в суть этого определения, а лучше сразу переходите к следующему абзацу. Обещаю, что после прочтения статьи вы вернетесь к этому определению и вас посетит мысль: «Ну да — так и есть! Тут все понятно!»
Приступим к основному занятию программиста — практике! В примере запрограммируем следующее: создадим класс Woman25
, который, используя дружественные функции и обычные методы класса, будет получать данные об объекте (имя и вес) и выводить их на экран. Методы и friend
-функции будут выполнять аналогичные действия. В этом и есть особенная польза данного примера — вы сможете посмотреть отличия в объявлении и определении дружественных функций от обычных методов класса. На основании полученных данных, программа даст пользователю совет относительно корректировки его веса. Ну что-же, лучше один раз увидеть…
#include <iostream> #include <string.h> using namespace std; class Woman25 { private: char *name;//имя int weight;//вес friend void setData(char *, int, Woman25&);//объявление дружественных функций friend void getData(Woman25&); public: Woman25()//конструктор { name = new char [20]; strcpy(name, "Норма"); weight = 60; } ~Woman25()//деструктор { delete [] name; cout << "!!! Деструктор !!!" << endl; } void setData(char*, int);//объявление методов класса void getData(); void advise(); }; void setData(char *n, int w, Woman25& object)//определяем friend-функцию setData { strcpy(object.name, n);//////////// object.weight = w; } void getData(Woman25& object)//определяем friend-функцию getData { cout << object.name << "\t: " << object.weight << " кг" << endl; } void Woman25::setData(char *n, int w)//определяем set-метод класса { strcpy(name, n); weight = w; } void Woman25::getData()//определяем get-метод класса { cout << name << "\t: " << weight << " кг" << endl; } void Woman25::advise()//определяем метод класса Совет (advise) { if(weight < 55){ //если вес меньше 55 кг cout << "Вам надо потреблять больше калорий!" << endl; cout << "=====================================" << endl << endl; }else if(weight >= 55 && weight <= 65){ //если вес в пределах 55-65 кг cout << "Ваш вес в норме!" << endl; cout << "=====================================" << endl << endl; }else { //если вес > 65 кг cout << "Вам надо ограничивать себя в еде!" << endl; cout << "=====================================" << endl << endl; } } int main() { setlocale(LC_ALL, "rus"); Woman25 Norm; //создаем объект Norm, сработает конструктор и weight будет = 60, name - Норма Norm.getData(); //вызов метода класса cout << "=====================================" << endl << endl; Woman25 Anna; //второй объект Anna.setData("Анна", 100); Anna.getData(); Anna.advise(); Woman25 Inna; //третий объект setData("Инна", 50, Inna); getData(Inna); Inna.advise(); return 0; }
Комментарии к исходному коду. В строках 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;
— вызывая дружественные функции. Как видите, вызываются они как функции, которые не принадлежат классу. Объект класса мы передаем, как параметр.
Запускаем программу и видим следующее.
Норма : 60 кг
=====================================
Анна : 100 кг
Вам надо ограничивать себя в еде!
=====================================
Инна : 50 кг
Вам надо потреблять больше калорий!
=====================================
!!! Деструктор !!!
!!! Деструктор !!!
!!! Деструктор !!!
Для продолжения нажмите любую клавишу . . .
И friend-функции, и методы класса справились со своими задачами, а мы с вами увидели отличия в их объявлении и определении. Так же мы увидели, что деструктор класса, как положено, сработал 3 раза (т.е. при уничтожении каждого созданного объекта). Если у вас возникает вопрос «Собственно, а зачем все это, если методы класса и так прекрасно работают?» — это хорошо! Ответ такой — «Представьте, что у нас есть еще десяток классов. Например Girl6_7
, Girl8_9
, Man25
и т.д., Используя дружественные функции, нам не придется для каждого класса определять set
и get
-методы. Это в нашей программе они короткие! А если бы они занимали 20 — 30 строк? А так достаточно определить метод в одном из классов или вообще определить функцию, как глобальную, а в остальные классы прописать ее прототип, как дружественной функции (используя слово friend
). Мы экономим массу времени и наш код становится намного короче.»
Теперь, как всегда, немного теории, для закрепления материала:
- Дружественная функция может располагаться в любом поле класса –
private
,public
илиprotected
. Она при любых обстоятельствах будет иметь доступ кprivate
-элементам класса и, даже если она сама находится в полеprivate
(как в нашем примере), к ней можно будет обратиться вне класса, не используя специальных методов. - Когда мы определяем дружественную функцию, элементы класса необходимо явно передавать в нее в виде параметров функции. Так как она не является компонентом класса, она не получает указатель
this
. - В виде параметра, в дружественную функцию так же надо передать указатель или ссылку на объект класса. Иначе она не увидит данные какого класса ей принять и обработать.
- Функция может использоваться, как дружественная к нескольким классам.
- Вызываются дружественные функции, как обычные функции. Т.е не используется такой способ —
Объект_класса.функция()
. После внесения всех необходимых параметров в нее при вызове, она сама увидит с элементами какого класса и объекта надо работать.
Вот и все для начала. Вопросы, предложения и замечания по теме ждем в комментариях к этой статье.
Комментарии
keniger
Получается, что константный массив передается аргументом в функцию, которая имеет параметр- указатель! Так не прокатит. Нужно передавать имя массива, и только так у меня и заработало!!!
keniger
Доброго времени суток! В строке Anna.setData(
"Анна"
, 100);
компилятор выдаетошибку: аргумент типа "const char*" не совместим с параметром "char*"!
Очень надеюсь на помощь!