Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
для шпор(печатаем 8 стр на листе).docx
Скачиваний:
1
Добавлен:
25.12.2019
Размер:
2.86 Mб
Скачать

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

Спецификаторы доступа класса позволяют указывать, могут ли функции вне определенного вами класса обращаться к его элементам. Может, однако, случиться, что вам потребуется обеспечить определенной функции или классу доступ к элементам вашего класса, специфицированным как private или protected. Для обеспечения такой возможности используется ключевое слово friend. Вы можете разрешить элементам другого класса (anotherClass) полный доступ к элементам вашего класса (myClass), объявленным как private или protected, включив в определение вашего класса описание friend. Например:

class myClass { friend class anotherClass; };

Аналогично вы можете разрешить обычной функции или функции-элементу другого класса полный доступ к элементам класса с помощью описания friend. Например:

class myClass { friend void anotherClass::MemberFuncName(int); friend void regularFuncName(double); };

К друзьям и дружественности применимы следующие правила: • на описания friend не влияют спецификаторы publicprotected или private; • описания friend не взаимны: если А объявляет В другом, то это не означает, что А является другом для В; • дружественность не наследуется: если А объявляет В другом, классы, производные от В, не будут автоматически получать доступ к элементам А; • дружественность не является переходным свойством: если А объявляет В другом, классы, производные от А, не будут автоматически признавать дружественность В. Обычное объявление функции-члена гарантирует три логически разные вещи: • во-первых, функция имеет право доступа к закрытой части объявления класса; • во-вторых, функция находится в области видимости класса; • в-третьих, функция должна вызываться для объекта класса, то есть имеется указатель this, Объявив функцию-член как static, мы придаем ей только первые два свойства. Объявив функцию как friend, мы наделяем ее только первым свойством. Так же как и функции-члены, функции-друзья явно указываются в объявлении класса, друзьями которого они являются. Поэтому они в той же мере являются частью интерфейса класса, в какой ею являются функции-члены. Так же как и объявление члена, объявление friend не добавляет новое имя в охватывающую область видимости. Например:

class Matrix { friend class Xform; friend Matrix invert (const Matrix&); … }; Xform x; /* ошибка: в текущей области видимости нет имени Xform */ Matrix (*p)(const Matrix&) = &invert; /* ошибка: в текущей области видимости нет имени invert */ Класс-друг должен быть предварительно объявлен в охватывающей области видимости или определен в области видимости, непосредственно охватывающей класс, объявивший его другом. При этом не принимаются во внимание области видимости вне области видимости самого внутреннего охватывающего пространства имен. Например:

class AE {…}; // не друг класса Y namespace N { class X {…}; // друг класса Y  class Y  { friend class X; friend class Z; friend class AE; }; class Z {…}; // друг класса Y }; Функцию-друга можно явно объявить точно так же, как и класс-друг, или ее поиск осуществляется по типам ее аргументов так, как будто она была объ-явлена вне класса, но в области видимости, непосредственно охватывающей класс. Например:

void f(Matrix &m) { invert(m); // функция invert – друг класса Matrix } Из этого следует, что функция-друг класса должна быть либо явно объявлена в охватывающей области видимости, либо иметь аргументы этого класса. В противном случае функцию-друга вызывать нельзя. Например:

class X { friend void f(); /* бесполезно, т.к. в этой области видимости нет имени f()*/ friend void h(const X&); /* можно найти по типу ар-гумента */ }; void g(const X &x) { f(); // нет имени f() в области видимости h(x); // функция h(x) – друг класса X }

Члены класса-члена не имеют свободного доступа к членам внешнего класса. Аналогично члены внешнего класса не имеют свободного доступа к членам вложенного класса; нужно соблюдать обычные правила доступа. Например:

class Outer //внешний { typedef int T; int i; public: int i2; static int s; class Inner // внутренний { int x; T y; // ошибка: Outer::T – закрытый public: void f(Outer *p, int v); }; int g(Inner *p); }; void Outer::Inner::f(Outer *p, int v) { p->i = v; // ошибка: Outer::i – закрытый p->i2 = v; // правильно: Outer::i2 – открытый } int Outer::g(Outer *p) { p->f(this, 2); // правильно: Inner::f – открытый return p->x; // ошибка: Inner::x – закрытый }

Однако часто полезно позволить классу-члену обращаться к его внешнему классу. Этого можно добиться, сделав член другом. Например:

class Outer  { typedef int T; int i; public: class Inner; /* предварительное объявление класса члена */ friend class Inner; /* разрешаем доступ к Outr::Inner */ class Inner  { int x; T y; // правильно: Inner – друг public: void f(Outer *p, int v); }; }; void Outer::Inner::f(Outer *p, int v) { p->i = v; // правильно: Inner – друг }