Среда, 08.01.2025, 20:52
Главная Регистрация RSS поиск
Приветствую Вас, Гость
Меню сайта
Категории раздела
HTML [44]
Visual C++ и MFC [21]
c++ [78]
php [19]
Javascript [15]
C# [51]
загрузки [0]
XNA [10]
создание игр с помощью xna
Наш опрос
Каким языком программирования вы увлекаетесь
Всего ответов: 2420
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Реклама
Главная » Статьи » c++

Дружественные функции (Friend Functions)

Итак, мы уже познакомились с одним из основных правил ООП: поля-данные (переменные) класса должны быть защищены от внешнего воздействия, то есть доступ к ним можно получить только с помощью функций-членов данного класса.

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

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

Для решения подобного круга задач в С++ и существует возможность описания функции, функции-члена другого класса как дружественной (friend). Класс может предоставлять особые привилегии определенным внешним функциям или функциям-членам другого класса. Вот эти функции и получили название дружественных. Рассмотрим как работает этот механизм.

Для описания функции (функции-члена) дружественной тому или иному классу, необходимо в описании этого класса объявить дружественную функцию, используя ключевое слово friend. Причем, обратите внимание, не играет никакой роли в каком из разделов класса Вы разместите объявление дружественной функции.

Согласитесь, небольшие нарушения ограничения доступа могут быть весьма полезны, если их использовать с достаточной осторожностью. Словосочетание "с достаточной осторожностью" обозначает, что функция или класс, пользующиеся привилегиями доступа должны быть очень тесно связаны с тем классом, который наделил их этими привилегиями. Главной целью использования дружественных функций и классов является повышение эффективности программы. Дружественные функции и классы могут осуществлять прямой доступ к закрытым полям объекта без использования функций-членов этого класса. Рассмотрим пример использования дружественной функции (объяснение приводится в коментариях):

#include <iostream.h>

class A //интерфейс (протокол) класса A
{
//закрытая область данных
private: //хотя, по умолчанию, действует модификатор 
 //доступа private
 //для читабельности программы мы его укажем явно
 int pole; //вот эта переменная и будет нашей подопытной

//открытая область данных
public:
 A(){ //конструктор по умолчанию
 pole=55;//инициализация закрытой переменной
 //выведем на экран сообщение, что 
 //конструктор отработал
 cout<<"Constructor A\n";
 }

//объявление дружественной функции которая принимает экземпляр класса A
friend void ourfriend(const A &f); 
}; 

//раз объявили дружественную функцию, то давайте уже и реализуем ее
void ourfriend(const A &f)
{
//итак, для демонстрации работы дружественной функции, мы обратимся 
//к закрытой переменной pole экземпляра класса f и выведем ее 
//значение на экран.
 cout<<"\nWow! We have free access to private members,\n"; 
 cout<<"pole ="<<f.pole<<endl;
}

void main()
{
 A example; //создали экземпляр класса А - example
 ourfriend(example); //собственно тестирование дружественной
 //функции
}

Результат выполнения программы:

Constructor A

Wow! We have free access to private members,
pole =55

Итак, мы смогли получить доступ к закрытой переменной класса из функции (которая, при этом, не является членом класса), используя механизм дружественных функций. Заметим, что при работе с дружественной функцией следует помнить о том, что она не имеет указателя this класса в котором она объявлена как дружественная. Почему?.. Ответ мы найдем в самом смысле использования дружественных функций: дружественная функция не является членом этого класса.

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

friend class имя_класса;

Может ли функция быть дружественной нескольким классам? Да, но если функция дружественна нескольким классам, то надо добавить это описание во все классы, к внутренним данным которых будет производиться обращение. Подозреваем, что у Вас возник вопрос: "А как же быть с защищенностью данных, если можно получить доступ к данным напрямую?" Не волнуйтесь, с точки зрения использования механизма инкапсуляции (напомним, под этим понятием мы подразумеваем сокрытие данных) защищенность данных сохраняется, поскольку полностью исключается всякая возможность доступа к данным так, чтобы объект не был осведомлен об этом. То есть в дружественных функциях мы свободно работаем только с экземплярами даного класса, но не с самим классом.

Снова, рассмотрим пример, но на этот раз, в роли дружественных функций у нас будут выступать функции-члены.

#include <iostream.h>

class A //интерфейс (протокол) класса A
{
//открытая область данных
public:
 A(){ //конструктор по умолчанию
 cout<<"Constructor A\n";
 //конструктор отработал
 }
//функция-член FriendToB() будет использоваться как 
//дружественная функция для класса B
 void FriendToB();
 void SomeFunction();
}; 

class B //интерфейс (протокол) класса B
{
//область закрытых данных
private:
 int i,j; //наши "подопытные" закрытые переменные;
 //если сможем обратиться к этим переменным
 //из дружественной функии FriendToB(), то
 //"опыт удался"
//область открытых данных
public:
 B(){
 i=10; //произведем инициализацию
 j=17; 
 cout<<"Constructor B\n";
 //конструктор отработал
 }
//укажем, что класс B имеет дружественную функцию, которая 
//является функцией-членом класса A
 friend void A::FriendToB();
};

class C //интерфейс (протокол) класса C
{
//область закрытых данных
private:
 int k,n; //закрытые
//область открытых данных
public:
 C(){
 k=4; //начальная инициализация
 n=7;
 cout<<"\n\nConstructor C\n";
 //конструктор отработал
 }
//укажем что ВСЕ функции-члены класса A, являются
//дружественными для даного класса
 friend class A;
};

//действительно ли может функция-член одного
//класса обращаться к закрытым данным другого 
//класса. Давайте проверим!
void A::FriendToB()
{
 B example; //объявим экземпляр класса B
 cout<<"So, variables' value are:\n";
//и произведем доступ к закрытым данным
 cout<<"example.i="<<example.i;
 cout<<"\nexample.j="<<example.j<<endl;
}


//действительно ли после объявления в классе С
//friend class A; -все функции-члены класса A
//станут дружественными функциями для класса С?
//Ведь мы нигде не указывали, что функция-член 
//SomeFunction() является дружественной классу С.
//Надо проверить...

void A::SomeFunction()
{
 C temp; //объявим экземпляр класса С
 cout<<"And now we are testing friend class...\n";
//и произведем доступ к закрытым данным
 cout<<"temp.k="<<temp.k;
 cout<<"\ntemp.n="<<temp.n<<endl;
}


void main()
{
 A test; //создали экземпляр класса А - example
//собственно тестирование дружественной функции классу B
 test.FriendToB();
//проверим, все ли функции-члены класса A являются дружественными
//классу С (на примере, функции SomeFunction)
 test.SomeFunction();
}

Вот что будет выведено на экран, в результате выполнения программы:

Constructor A
Constructor B
So, variables' value are:
example.i=10
example.j=17


Constructor C
And now we are testing friend class...
temp.k=4
temp.n=7

Итак, если функция или функция-член объявлены как дружественные, то такие функции или функции-члены такого класса могут осуществлять непосредственный доступ ко всем (private, protected, public) данным (полям-данным и функциям-членам) класса, для которого они дружественны.


Категория: c++ | Добавил: slava (30.05.2011)
Просмотров: 11880 | Рейтинг: 4.2/5
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]