Описание одного класса как друг другого класса дополнительно подразумевает, что частные и защищенные члены класса, предлагающего дружбу, могут использоваться в классе, получающем ее, например:

class X {

 enum { a=100 };

 friend class Y;

};

class Y {

 int v[X::a]; // Y друг класса X

};

class Z {

 int v[X::a]; // ошибка: X::a недоступно

};

Если класс или функция, объявленные как друзья, не были описаны, их имена попадают в ту же область видимости, что и имя класса, содержащего описание friend (§R.9.1).

Функция, появившаяся первый раз в описании friend, считается эквивалентной функции, описанной как extern (§R.3.3, §R.7.1.1).

Если функция-друг определена в описании класса, она считается функцией со спецификацией inline и к ней применимо правило переноса определения функции для функций-членов (§R.9.3.2). Функция-друг, определенная в описании класса, относится на лексическом уровне к области видимости этого класса. Для функции-друга, определенной вне класса, это не так.

На описание friend не влияет указание спецификаций-доступа (§R.9.2).

Понятие дружбы не является ни наследуемым, ни транзитивным.

Подтвердим это примером:

class A {

 friend class B;

 int a;

};

class B {

 friend class C;

};

class C {

 void f(A* p);

 {

  p-›a++; // ошибка: C не друг класса A, хотя

   // является другом друга класса A

 }

};

class D: public B {

 void f(A* p)

 {

  p-›a++; // ошибка: D не друг класса A, хотя

   // является производным друга класса A

 }

};

<p>R.11.5 Доступ к защищенным членам</p>

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

class B {

protected:

 int i;

};

class D1: public B {

};

class D2: public B {

 friend void fr(B*, D1*, D2*);

 void mem(B*, D1*);

};

void fr(B* pb, D1* p1, D2* p2)

{

 pb-›i = 1; // недопустимо

 p1-›i = 2; // недопустимо

 p2-›i = 3; // нормально (обращение через D2)

}

void D2::mem(B* pb, D1* p1)

{

 pb-›i = 1; // недопустимо

 p1-›i = 2; // недопустимо

 i = 3; // нормально (обращение через this)

}

void g(B* pb, D1* p1, D2* p2)

{

 pb-›i = 1; // недопустимо

 p1-›i = 2; // недопустимо

 p2-›i = 3; // недопустимо

}

<p>R.11.6 Доступ к виртуальным функциям</p>

Правила доступа (§R.11) к виртуальной функции определяются ее описанием и на них не влияют правила доступа к к функции, которая позднее будет подавлять ее. Приведем пример:

class B {

public:

 virtual f();

};

class D: public B {

private:

 f();

};

void f()

{

 D d;

 B* pb = &d

 D* pd = &d

 pb-›f(); // нормально: B::f() общий член

  // вызывается D::f()

 pd-›f(); // ошибка: D::f() частный член

}

Права доступа проверяются при самом вызове, используя тип выражения, обозначающее объект, для которого вызывается функция-член (в примере выше это B*). Доступ к функции-члену в классе, где она определена (D в примере выше), в общем случае неизвестен.

<p>R.11.7 Множественный доступ</p>

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

class W { public: void f(); };

class A: private virtual W {};

class B: public virtual W {};

class C: public A, public B {

 void f() { W::f(); } // нормально

};

Поскольку W::f() доступно в C::f() по пути, связанному с общим наследованием из B, обращение является законным.

<p>R.12 Специальные функции-члены</p>
Перейти на страницу:

Похожие книги