
Лекции по ППВиС (17 шт, ppt) Романов ВИ 2011 1ый семестр / Л4_Классы и сокрытие данных_Дружественные функции
.ppt
21
Атрибут доступа
Каждая функция и элемент данных, описанные внутри класса, имеют атрибут доступа:
public (открытый) – доступ к элементам класса может быть осуществлен
как из самого класса, так и извне класса
private (закрытый) – доступ к элементам класса может быть
осуществлен только из функций самого класса.
protected (защищенный) – имеет смысл при наследовании классов и будет рассмотрен позже
По умолчанию все элементы класса имеют атрибут private . Атрибут задается соответствующим ключевым словом и символом «:». Действие атрибута сохраняется до следующего атрибута или до закрывающей фигурной скобки в объявлении класса.
Для структур по умолчанию все элементы класса имеют атрибут public
.

22
Инкапсуляция – идея
class My_cl { public:
void Set(int n);
. . .
protected :
. . .
private:
. . .
};
//public – виден и доступен всем
//protected – виден и доступен только из базового класса и из
// производных классов
// private – виден и доступен только из базового класса
Где либо в программе: My_cl clMy; clMy.Set(a);

23
Инкапсуляция - пример
class My_str { public:
void Get(int n); int Var1;
. . .
protected : int Var2;
. . .
private: int Var3;
. . .
};
Где либо в программе: My_str::Get(a);
. . .
void My_str::Get(int a)
{
int V;
};
void main()
{
My_str Obj; |
” cannot access protected |
Obj.V1 = 1; |
member” |
Obj.V2 = 1; // Ошибка |
|
Obj.V3 = 1; // Ошибка |
|
} |
cannot access private member |

24
Дружественные функции – общие положения
При написании программ часто встречается ситуация, при которой необходимо иметь доступ к закрытым компонентам классов. При использовании обычных внешних функций это невозможно – возникают
ошибки компиляции. Для решения данной задачи в С++ применяются так называемые дружественные функции. Для этого используется спецификатор friend. Отмеченная им функция:
не является методом-функцией этого класса;
имеет доступ ко всем компонентам этого класса (даже private).
Прототипы таких функций задаются в теле описания класса. Само тело такой функции может быть описано где угодно – и в классе, и за его пределами.
Лучше производить определение дружественной функции за пределами класса, подчеркивая тем самым тот факт, что функция не является компонентом класса.

25
Дружественная функция (пример)
#include <iostream.h>
using namespace std; class C {
int n; public:
C(int n) {this->n = n; }; // конструктор void print() { cout << "n="<< endl; } friend void inc(C&);
};
void inc (C& obj) { obj.n++; }
void main ()
{ C obj(4); obj.print(); inc(obj); obj.print(); }

26
Дружественная функция – объявление и вызов
Для того, чтобы дружественная функция осуществляла доступ к компонентам класса необходимо в качестве аргумента передавать либо указатель либо ссылку на объект.
Объявлять прототип дружественной функции можно в любом месте описания класса.
Атрибуты доступа для дружественных функций не играют роли.
Так как дружественная функция не является компонентом класса ее нельзя вызвать с использованием имени объекта или указателя на него. Вызов осуществляется обычным способом.
В С++ одна функция не может быть компонентой двух различных классов. Иногда возникает необходимость организации доступа к локальным данным нескольких классов из одной функции. Это еще одна (если не главная!) причина для использования дружественных функций.

27
Дружественная функция (пример 2)
#include <iostream.h>
class B; // ссылка вперед – некоторый // аналог прототипа функции
class C { int k; public:
C(int n) { k = n; }; // конструктор friend char* equal (const C&, const B&);
};
class B { int k; public:
B(int n) { k = n*n; }; // конструктор friend char * equal (const C&, const B&);
};
char* equal (const C &o1, const B &o2)
{
if (o1.k==o2.k) return ("C.k=B.k"); return ("C.k<>B.k");
}
void main ()
{
C obj1(4); B obj2(0);
cout << equal(obj1, obj2) << endl;
}

28
Спецификатор friend классов
Дружественные функции могут быть перегружены.
Любой метод одного класса может быть дружественным по отношению к другому классу. В этом случае для доступа к компонентам классов в качестве аргументов необходимо лишь передать ссылки или указатели на объекты, для которой функция является дружественной.
Предположим, что для предыдущего примера функция equal является методом класса С и дружественной по отношению к классу B.
Тогда описания этой функции будут выглядеть так:
В классе С:
char* equal (const B&);
В классе B:
friend char* C::equal (const B&);

29
Спецификатор friend классов
#include <iostream.h> class A {
int i; public:
friend class B;
A( ) : i(1) { }; // конструктор ~A( ) { };
void f1_A(const B&); };
class B { int j; public:
friend class A;
B( ) : j(2) { }; // конструктор ~B( ) { };
void f1_B(const A& a)
{ cout << a.i + j << endl; }
};
Дружественными могут
быть не только функции, но и классы.
void A::f1_A(const B& b)
{ cout << i <<" "<<b.j << endl; }
void main ( )
{
A aa; B bb;
aa.f1_A(bb); bb.f1_B(aa);
}
Результат выполнения программы:
1 2
3

30
Спецификатор friend классов - свойства
Основные свойства и правила использования спецификации friend.
friend-функции не являются компонентами класса, но имеет доступ ко всем его компонентам;
friend-функции не имеют доступа к указателю this;
friend-функции не наследуются в производных классах;
отношение friend не является ни симметричным, ни транзитивным:
A друг B не значит, что B друг A!
Если A друг B, а B друг C, то это не значит, что A друг C!
друзьями класса можно определить перегруженные функции. Каждая перегруженная функция, используемая как friend для некоторого класса, должна быть явно объявлена в классе со спецификатором friend.