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
  • «дружественная» функция не есть компонентой того класса к которому она «дружественна»
  • «дружественная» функция может быть «дружественной» по отношению к нескольким классам


Связанные темы