C++. “Друзі” класу. Дружні класи та дружні функції. Ключове слово friend. Приклади

“Друзі” класу. Дружні класи та дружні функції. Ключове слово friend. Приклади


Зміст


Пошук на інших ресурсах:

1. Для чого використовуються так звані “друзі” класу?

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

Якщо клас A оголошується “дружнім” до класу B, то об’єкти класу A мають доступ до усіх членів даних і методів класу B. Якщо функція оголошується “дружньою” до деякого класу, то у цій функції також є необмежений доступ до членів даних та методів цього класу.

2. Яка загальна форма оголошення “дружнього” класу? Ключове слово friend

Щоб оголосити “дружній” клас до даного класу, використовується ключове слово friend. Загальна форма оголошення “дружнього” класу до даного має вигляд:

class CClass
{
    // ...

    friend class CFriendClass;

    // ...
};

class CFriendClass
{
    // ...
};

де

  • CClass – клас, в якому оголошується “дружній” клас CFriendClass. Усі змінні (навіть і private) та методи цього класу є доступними для об’єктів класу CFriendClass;
  • CFriendClass – клас, який є “дружнім” до класу CClass. Оголошення “дружнього” класу CFriendClass до класу CClass може бути в будь-якому місці тіла класу – у межах оголошення класу (між фігурними дужками { }).

3. Яка загальна форма оголошення “дружньої” функції до деякого класу?

Оголошення “дружньої” функції до класу починається з ключового слова friend. Загальна форма оголошення “дружньої” функції до класу має вигляд:

friend type FunName(parameters);

де

  • FunName – ім’я “дружньої” функції;
  • type – тип, що повертається функцією FunName();
  • parameters – параметри “дружньої” функції. Щоб отримати об’єкт потрібного класу у функції FunName() доцільно передати у цю функцію посилання (або покажчик) на об’єкт цього класу.

Якщо потрібно оголосити “дружню” функцію у деякому класі, то загальний вигляд такого оголошення наступний:

class CClass
{
    // ...

    friend type FunName(parameters);

    // ...
};

де

  • FunName – ім’я “дружньої” функції;
  • type – тип, що повертається функцією FunName();
  • parameters – параметри “дружньої” функції.

4. В якому місці (розділі) оголошення класу можна оголошувати дружній клас або функцію?

Оголошувати “дружній” клас або функцію до заданого класу можна у будь-якому місці чи розділі класу в межах його оголошення (між фігурними дужками { } ).






5. Скільки “дружніх” функцій та “дружніх” класів можна оголошувати у тілі класу?

Скільки завгодно.

6. Як отримати об’єкт потрібного класу у “дружній” функції, щоб мати доступ до усіх його членів? Приклад

Щоб отримати об’єкт потрібного класу у функції доцільно передати у цю функцію посилання (або покажчик) на об’єкт цього класу.

Наприклад.

Нехай задано клас з іменем CMyClass. Потрібно оголосити “дружню” функцію до цього класу, яка має ім’я FriendFun(). Функція повертає параметр типу int.

Оголошення класу CMyClass та “дружньої” функції у класі має вигляд:

// оголошення класу CMyClass, в якому є "дружня" функція FriendFun()
class CMyClass
{
    // тіло класу
    // ...

    friend int FriendFun(CMyClass &);

    // ...
};

Реалізація “дружньої” функції FriendFun():

int FriendFun(CMyClass & mc)
{
    // використання об'єкту класу mc для доступу до членів класу CMyClass
    // ...
};

7. Приклад оголошення класу, що є дружнім до іншого класу

Нехай задано клас Number, що містить цілочисельну величину. Також задано клас RangeNum, який містить величину Number але в межах заданого діапазону.

Щоб з класу RangeNum можна було мати доступ до приватної змінної num класу Number, клас RangeNum оголошується “дружнім” до класу Number.

Лістинг класів Number та RangeNum має вигляд:

// клас, що реалізує ціле число
class Number
{
    // оголошення дружнього класу RangeNum до класу Number
    friend class RangeNum;
    int num;

    public:
    // конструктори
    Number() { num = 0; }
    Number(int num) { this->num = num; }
};

// оголошення класу RangeNum, який тримає число Number в заданих межах
class RangeNum
{
    Number num; // об'єкт класу Number - просте ціле число
    int min; // нижня межа числа num
    int max; // верхня межа числа num

    public:
    // конструктор класу
    RangeNum()
    {
        // доступ до private-члена класу Number, тому що RangeNum є дружнім до Number
        num.num = 0;

        // встановлення діапазону 0..99 за замовчуванням
        min = 0;
        max = 99;
    }

    // методи доступу
    int GetNum(void)
    {
        return num.num; // доступ до приватного члена з "дружнього" класу Range
    }

    void SetNum(int nnum)
    {
        num.num = nnum; // доступ до приватного члена з "дружнього" класу Range
        if (num.num>max) num.num = max-1;
        if (num.num<min) num.num = min;
    }

    // встановлення діапазону для num в заданих межах
    void SetRange(int min, int max)
    {
        this->min = min;
        this->max = max;
        if (num.num>max) num.num = max-1; // знову доступ через дружній клас
        if (num.num<min) num.num = min;
    }
};

Використання класів у іншому програмному коді

int _tmain(int argc, _TCHAR* argv[])
{
    // об'єкт класу Range
    RangeNum r;
    int d;
    d = r.GetNum(); // d = 0

    r.SetRange(100, 200);
    r.SetNum(101);
    d = r.GetNum(); // d = 101

    r.SetRange(10, 20); // корегується значення num
    d = r.GetNum(); // d = 19
    r.SetNum(-10);
    d = r.GetNum(); // d = 10

    return 0;
}

Якщо у класі Number в оголошенні “дружнього” класу RangeNum

friend class RangeNum;

забрати ключове слово friend, то у конструкторі та усіх методах класу при доступі до num.num компілятор видасть помилку:

Number::num: cannot access private member declared in class 'Number'

8. Приклад оголошення функції, що є дружньою до іншого класу

У прикладі оголошується клас Radius, що містить величину радіуса деякої геометричної фігури. У класі оголошуються:

  • одна прихована (private) змінна radius;
  • методи Get() та Set() для доступу до змінної radius;
  • дві зовнішні “дружні” функції GetLength() та GetArea();
  • один “дружній” клас Volume. У класі Volume оголошується зовнішній (public) метод GetVolume(), який повертає об’єм кулі заданого радіуса.

Лістинг додатку типу “Win32 Console Application” наступний:

#include "stdafx.h"
#include <iostream>

using namespace std;

// клас, що реалізує величину радіуса геометричної фігури
class Radius
{
    private:
    double radius; // прихована змінна radius

    // оголошення "дружніх" функцій - в будь-якому розділі класу Radius
    friend double GetLength(Radius &);
    friend double GetArea(Radius &);

    // оголошення "дружнього" класу
    friend class Volume;

    public:
    // методи доступу до radius
    double Get(void) { return radius; }
    void Set(double nradius) { radius = nradius; }
};

// "дружні" функції до класу Radius
// довжина кола
double GetLength(Radius & r)
{
    // доступ до private-члена radius класу з "дружньої" функції GetLength()
    return (double)(2 * r.radius * 3.1415);
}

// площа круга
double GetArea(Radius & r)
{
    // доступ до private-члена radius класу з "дружньої" функції GetArea()
    return (double)(r.radius * r.radius * 3.1415);
}

// оголошення "дружнього" класу
class Volume
{
    public:
    // клас містить тільки одну функцію Volume
    double GetVolume(Radius * r) // функція отримує покажчик на Radius
    {
        // доступ до private-члена класу Radius з "дружнього" класу Volume
        return (double)(4.0 / 3.0 * 3.1415 * r->radius * r->radius * r->radius);
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    // об'єкт класу Radius
    Radius r;
    Volume v;
    double len, area, vol;

    r.Set(3);

    // виклик зовнішньої "дружньої" функції GetLength()
    len = GetLength(r); // передача об'єкту класу Radius за посиланням

    // виклик зовнішньої "дружньої" функції GetArea()
    area = ::GetArea(r); // передача за посиланням
 
    // виклик функції "дружнього" класу v
    vol = v.GetVolume(&r); // передача за покажчиком

    cout << "Length = " << len << endl; // Length = 9.4245
    cout << "Area = " << area << endl; // Area = 28.2735
    cout << "Volume = " << vol << endl; // Volume = 113.094

    return 0;
}

Як видно з вищенаведеного коду, “дружні” функції отримують вхідним параметром посилання або покажчик на об’єкт класу, до членів даних та методів якого вони мають необмежений доступ.

9. Особливості застосування “дружніх” функцій

Про “дружні” функції можна сказати наступне:

  • “дружня” функція не отримує покажчик this класу в якому вона реалізована
  • об’єкти класів передаються в “дружню” функцію явно через параметри
  • “дружні” функції не викликаються через об’єкт класу, друзями якого вони є
  • на “дружні” функції не діють специфікатори доступу private, protected, public
  • “дружня” функція не є компонентою того класу до якого вона “дружня”
  • “дружня” функція може бути “дружньою” по відношенню до декількох класів


Зв’язані теми