Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
+ООП_Навч_посібник.doc
Скачиваний:
7
Добавлен:
01.07.2025
Размер:
6.58 Mб
Скачать

13.1. Поняття про функції-"друзі" класу

У мові програмування C++ існує можливість доступу до закритих членів класу функціями, які не є членами цього класу. Для цього достатньо оголосити ці функції дружніми до цього класу. Щоб зробити функцію "другом" класу, потрібно помістити її прототип в public-розділ оголошення класу і попередити його ключовим словом friend. Наприклад, цей фрагмент коду функції frnd() оголошується "другом" класу demoClass:

class demoClass { // Оголошення класового типу

//...

public:

friend void frnd(demoClass obj); // Функція-"друг" класу

//...

};

Як бачимо, ключове слово friend передує решті частини прототипу функції. Одна і та ж функція може бути "другом" декількох класів.

Ключове слово friend надає функції, яка не є членом класу, доступ до його закритих членів.

Розглянемо короткий приклад, у якому функція-"друг" класу використовується для доступу до закритих членів класу myClass.

Код програми 13.1. Демонстрація механізму використання функції-"друга" класу для доступу до закритих членів класу

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

class myClass { // Оголошення класового типу

int a, b;

public:

// Оголошення параметризованого конструктора

myClass(int izm, int jzm) {a = izm; b = jzm;}

// Функція fun_sum() – "друг" класу myClass.

friend int fun_sum(myClass s_ob);

};

// Функція fun_sum() не є членом ні одного класу.

int fun_sum(myClass s_ob)

{

/* Оскільки функція fun_sum() – "друг" класу myClass,

то вона має право на прямий доступ до його членів-даних a i b. */

return s_ob.a + s_ob.b;

}

int main()

{

myClass S_ob(3, 4); // Створення об'єкта класу

cout << "Suma= " << fun_sum(S_ob) << "\n";

getch(); return 0;

}

У наведеному прикладі функція fun_sum() не є членом класу myClass. Проте вона має повний доступ до private-членів класу myClass. Зокрема, вона може безпосередньо використовувати значення s_ob.а і s_ob.b. Зверніть також увагу на те, що функція fun_sum() викликається звичайним способом, тобто без прив'язування до імені об'єкта (і без використання оператора "крапка"). Оскільки вона не є функцією-членом класу, то під час виклику її не потрібно кваліфікувати з вказанням імені об'єкта1. Зазвичай "дружній" функції як параметр передається один або декілька об'єктів класу, для яких вона є "другом". Робиться це так само як і у випадку передачі параметра функції fun_sum().

Незважаючи на те, що у наведеному прикладі ми не отримуємо ніякої корисності з оголошення "другом" функції fun_sum(), а не членом класу myClass, існують певні обставини, при яких статус функції-"друга" класу має велике значення. По-перше, функції-"друзі" можуть бути корисні для перевантаження операторів певних типів. По-друге, функції-"друзі" спрощують створення деяких функцій введення-виведення. Усі ці питання ми розглянемо згодом у цьому навчальному посібнику.

Третя причина частого використання функцій-"друзів" класу полягає у тому, що в деяких випадках два (або більше) класи можуть містити члени, які перебувають у взаємному зв'язку з іншими частинами програми. Наприклад, у нас є два різні класи, які під час виникнення певних подій відображають на екрані "спливаючі" повідомлення. Інші частини програми, які призначені для виведення даних на екран, повинні знати, чи є "спливаюче" повідомлення активним, щоб випадково не перезаписати його. Для уникнення цього у кожному класі можна створити функцію-члена, що повертає значення, за яким можна робити висновки про те, є повідомлення активним чи ні. Однак перевірка цієї умови вимагатиме додаткових витрат (тобто двох викликів функцій замість одного). Якщо статус "спливаючого" повідомлення необхідно перевіряти часто, то ці додаткові витрати можуть виявитися відчутними. Проте за допомогою функції, "дружньої" для обох класів, можна безпосередньо перевіряти статус кожного об'єкта, викликаючи для цього тільки одну і ту саму функцію, яка матиме доступ до обох класів. У таких ситуаціях функція-"друг" класу дає змогу написати більш ефективний програмний код. Цю ідею продемонструємо на прикладі такої програми.

Код програми 13.2. Демонстрація механізму використання функції-"друга" класу для перевірки статусу кожного об'єкта класу

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

const int IDLE=0;

const int INUSE=1;

class bClass; // Випереджувальне оголошення класу

class aClass { // Оголошення класового типу

int status; // IDLE, якщо повідомлення неактивне

// INUSE, якщо повідомлення виведене на екран.

//...

public:

void setStatus(int state) { status = state; }

friend int displey(aClass a_ob, bClass b_ob);

};

class bClass { // Оголошення класового типу

int status; // IDLE, якщо повідомлення неактивне

// INUSE, якщо повідомлення виведене на екран.

//...

public:

void setStatus(int state) { status = state; }

friend int displey(aClass a_ob, bClass b_ob);

};

// Функція displey() – "друг" для класів aClass і bClass.

int displey(aClass a_ob, bClass b_ob)

{

if(a_ob.status || b_ob.status) return 0;

else return 1;

}

int main()

{

aClass X_ob; // Створення об'єкта класу

bClass Y_ob; // Створення об'єкта класу

X_ob.setStatus(IDLE); // IDLE = 0

Y_ob.setStatus(IDLE);

if(displey(X_ob, Y_ob)) cout << "Екран вільний.\n";

else cout << "Відображається повідомлення.\n";

X_ob.setStatus(INUSE); // INUSE = 1

if(displey(X_ob, Y_ob)) cout << "Екран вільний.\n";

else cout << "Відображається повідомлення.\n";

getch(); return 0;

}

У процесі виконання програма відображає на екрані такі результати:

Екран вільний.

Відображається повідомлення.

Оскільки функція displey() є "другом" як для класу aClass, так і для класу bClass, то вона має доступ до закритого члена status, визначеного в обох класах. Таким чином, стан об'єкта кожного класу одночасно можна перевірити всього одним зверненням до функції displey().

Випереджувальне оголошення призначене для оголошення імені класового типу ще до визначення самого класу.

Звернемо Вашу увагу на те, що у цій програмі використовується випереджувальне оголошення (також називається випереджувальним посиланням) для класу bClass. Потреба у ньому пов'язана з тим, що оголошення функції displey() у класі aClass використовує посилання на клас bClass ще до його оголошення. Щоб створити випереджувальне оголошення для будь-якого класу, достатньо використовувати такий формат, як це представлено у наведеному вище коді програми.

"Друг" одного класу може бути членом іншого класу. Перепишемо попередню програму так, щоби функція displey() стала членом класу aClass. Звернемо Вашу увагу на використання оператора дозволу області видимості (або оператора дозволу контексту) під час оголошення функції displey() як "друга" класу bClass.

Код програми 13.3. Демонстрація механізму використання функції – члена одного класу і одночасно "друга" – іншого класу

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

const int IDLE=0;

const int INUSE=1;

class bClass; // Випереджувальне оголошення класу

class aClass { // Оголошення класового типу

int status; // IDLE, якщо повідомлення неактивне

// INUSE, якщо повідомлення виведене на екран.

//...

public:

void setStatus(int state) { status = state; }

int displey(bClass b_ob); // тепер це член класу aClass

};

class bClass { // Оголошення класового типу

int status; // IDLE, якщо повідомлення неактивне

// INUSE, якщо повідомлення виведене на екран.

//...

public:

void setStatus(int state) { status = state; }

friend int aClass::displey(bClass b_ob); // функція-"друг" класу

};

// Функція displey() -- член класу aClass і "друг" класу bClass.

int aClass::displey(bClass b_ob)

{

if(status || b_ob.status) return 0;

else return 1;

}

int main()

{

aClass X_ob; // Створення об'єкта класу

bClass Y_ob; // Створення об'єкта класу

X_ob.setStatus(IDLE); // IDLE = 0

Y_ob.setStatus(IDLE);

if(X_ob.displey(Y_ob)) cout << "Екран вільний.\n";

else cout << "Відображається повідомлення.\n";

X_ob.setStatus(INUSE); // INUSE = 1

if(X_ob.displey(Y_ob)) cout << "Екран вільний.\n";

else cout << "Відображається повідомлення.\n";

getch(); return 0;

}

Оскільки функція displey() є членом класу aClass, то вона має прямий доступ до змінної status об'єктів типу aClass. Отже, як параметр необхідно передавати функції displey() тільки об'єкти типу bClass.