Дружественные функции и классы

Последнее обновление: 10.03.2023

Дружественные функции - это функции, которые не являются членами класса, однако имеют доступ к его закрытым членам - переменным и функциям, которые имеют спецификатор private.

Для определения дружественных функций используется ключевое слово friend. Например, определим следующую программу:

#include <iostream>
 
class Auto
{
    friend void drive(const Auto&);
    friend void setPrice(Auto&, unsigned);
public:
    Auto(std::string autoName, unsigned autoPrice) 
    { 
        name = autoName; 
        price = autoPrice;
    }
    void print()
    {
        std::cout << name << " : " << price << std::endl;
    }
 
private:
    std::string name;   // название автомобиля
    unsigned price;  // цена автомобиля
};
 
void drive(const Auto &car) 
{ 
    std::cout << car.name << " is driven" << std::endl;
}
void setPrice(Auto &car, unsigned price)
{
    car.price = price;
}
 
int main()
{
    Auto tesla("Tesla", 5000);
    tesla.print();  //
    drive(tesla);
    setPrice(tesla, 8000);
    tesla.print();  //
}

Здесь определен класс Auto, который представляет автомобиль. У этого класса определены приватные закрытые переменные name (название автомобиля) и price (цена автомобиля). Также в классе объявлены две дружественные функции: drive (функция вождения автомобиля) и setPrice (функция назначения цены). Обе этих функции принимают в качестве параметра ссылку на объект Auto.

Когда мы объявляем дружественные функции, то фактически мы говорим компилятору, что это друзья класса и они имеют доступ ко всем членам этого класса, в том числе закрытым.

При этом для дружественных функций не важно, определяются они под спецификатором public или private. Для них это не имеет значения.

Определение этих функций производится вне класса. И поскольку эти функции являются дружественными, то внутри этих функций мы можем через переданную ссылку Auto обратиться ко всем его закрытым переменным.

Консольный вывод программы:

Tesla : 5000
Tesla is driven
Tesla : 8000

Определение дружественных функций в классе

Дружественные функции могут определяться в другом классе. Например, определим класс Person, который использует объект Auto:

#include <iostream>

class Auto; // объявление класса Auto, чтобы Person видел этот класс

class Person
{
public:
	Person(std::string p_name)
	{
		name = p_name;
	}
	void drive(const Auto&);
	void setPrice(Auto&, unsigned);

private:
	std::string name;
};

class Auto
{
    // объявление дружественных функций
    friend void Person::drive(const Auto&);
    friend void Person::setPrice(Auto&, unsigned);
public:
    Auto(std::string a_name, unsigned a_price) 
    { 
        name = a_name; 
        price = a_price;
    }
    void print()
    {
        std::cout << name << " : " << price << std::endl;
    }
 
private:
    std::string name;   // название автомобиля
    unsigned price;  // цена автомобиля
};
 
void Person::drive(const Auto &car) 
{ 
    std::cout << name << " drives " << car.name << std::endl;
}
void Person::setPrice(Auto &car, unsigned price)
{
    car.price = price;
}
 
int main()
{
    Auto tesla{"Tesla", 5000};
    Person tom{"Tom"};
    tom.drive(tesla);
    tom.setPrice(tesla, 8000);
    tesla.print();  
}

Вначале определен класс Person, который представляет человека. Однако поскольку класс Person использует класс Auto, то перед классом Person идет объявление класса Auto.

Две функции из класса Person принимают ссылку на объект Auto:

void drive(const Auto&);
void setPrice(Auto&, unsigned);

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

Класс Auto определяет дружественные функции с той же сигнатурой:

friend void Person::drive(Auto&);
friend void Person::setPrice(Auto&, unsigned);

Причем поскольку данные функции будут определены в классе Person, то названия этих функций предваряются префиксом "Person::".

И поскольку в этих функциях предполагается использовать объект Auto, то ко времени определения этих функций все члены объекта Auto должны быть известны, поэтому определения функций находятся не в самом классе Person, а после класса Auto. И так как эти функции определены в классе Auto как дружественные, мы можем обратиться в этих функциях к закрытым членам класса Auto.

Консольный вывод программы:

Tom drives Tesla
Tesla : 8000

Дружественные классы

В случае выше класс Person использует только две функции из класса Auto. Но допустим впоследствии возникла необходимость добавить в класс Auto еще ряд дружественных функций, которые будут определены в классе Person. Либо мы можем предполагать, что класс Person будет активно использовать объекты Auto. И в этом случае целесообразно определять не отдельные дружественные функции, а определить дружественным весь класс Person:

#include <iostream>

class Auto; // объявление класса Auto, чтобы Person видел этот класс

class Person
{
public:
	Person(std::string p_name)
	{
		name = p_name;
	}
	void drive(const Auto&);
	void setPrice(Auto&, unsigned);

private:
	std::string name;
};

class Auto
{
    // объявление дружественного класса
    friend class Person;
public:
    Auto(std::string a_name, unsigned a_price) 
    { 
        name = a_name; 
        price = a_price;
    }
    void print()
    {
        std::cout << name << " : " << price << std::endl;
    }
 
private:
    std::string name;   // название автомобиля
    unsigned price;  // цена автомобиля
};
 
void Person::drive(const Auto& car) 
{ 
    std::cout << name << " drives " << car.name << std::endl;
}
void Person::setPrice(Auto& car, unsigned price)
{
    car.price = price;
}
 
int main()
{
    Auto tesla{"Tesla", 5000};
    Person tom{"Tom"};
    tom.drive(tesla);
    tom.setPrice(tesla, 8000);
    tesla.print();  
}

Единственное, что в данном случае изменилось по сравнению с предыдущим примером - это то, что в классе Auto определение дружественных функций было заменено определением дружественного класса:

friend class Person;

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

Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850