Спецификаторы доступа класса позволяют указывать, могут ли функции вне определенного вами класса обращаться к его элементам. Может, однако, случиться, что вам потребуется обеспечить определенной функции или классу доступ к элементам вашего класса, специфицированным как 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 не влияют спецификаторы public,
protected или 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 – друг
}
|