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

 

  В этой статье мы расскажем о  следующем:

  • Что такое дружественные функции и, главное, зачем они нужны.
  • Чем отличаются обычные методы класса от дружественных функций класса.
  • Как объявлять и определять дружественные функции.
  • Как вызывать дружественные функции из главной 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; — вызывая дружественные функции. Как видите, вызываются они как функции, которые не принадлежат классу. Объект класса мы передаем, как параметр.

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

CppStudio.com

Норма : 60 кг
=====================================

Анна : 100 кг
Вам надо ограничивать себя в еде!
=====================================

Инна : 50 кг
Вам надо потреблять больше калорий!
=====================================

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

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

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

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

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

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

Практика

К сожалению, для данной темы пока нет подходящих задач. Если у вас есть таковые на примете, отправте их по адресу: admin@cppstudio.com. Мы их опубликуем!

Автор: Marienko L.
Дата: 16.01.2014
Поделиться:

Комментарии

  1. keniger

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

  2. keniger

    Доброго времени суток! В строке Anna.setData("Анна", 100); компилятор выдает

    ошибку: аргумент типа "const char*" не совместим  с параметром "char*"!

    Очень надеюсь на помощь!

Оставить комментарий

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