friend class Pal; // у Pal нет доступа к классам, производным от Base

};

class Pal {

public:

 int f(Base b) { return b.prot_mem; } // ok: Pal дружествен Base

 int f2(Sneaky s) { return s.j; }     // ошибка: Pal не

                                      // дружествен Sneaky

 // доступ к базовому классу контролируется базовым классом, даже в

 // объекте производного

 int f3(Sneaky s) { return s.prot_mem; } // ok: Pal дружествен

};

Факт допустимости функции f3() может показаться удивительным, но он непосредственно следует из правила, что все классы контролируют доступ к собственным членам. Класс Pal — друг класса Base, поэтому класс Pal может обращаться к членам объектов класса Base. Это относится и к встроенным в объект класса Base объектам классов, производных от него.

Когда класс объявляет другой класс дружественным, это относится только к данному классу, ни его базовые, ни производные классы никаких специальных прав доступа не имеют:

// у D2 нет доступа к закрытым или защищенным членам Base

class D2 : public Pal {

public:

 int mem(Base b)

  { return b.prot_mem; } // ошибка: дружба не наследуется

};

Дружественные отношения не наследуются; каждый класс сам контролирует доступ к своим членам.

Освобождение индивидуальных членов

Иногда необходимо изменить уровень доступа к имени, унаследованному производным классом. Для этого можно использовать объявление using (см. раздел 3.1):

class Base {

public:

 std::size_t size() const { return n; }

protected:

 std::size_t n;

};

class Derived : private Base { // заметьте, наследование закрытое

public:

 // обеспечить уровни доступа для членов, связанных с размером объекта

 using Base::size;

protected:

 using Base::n;

};

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

Объявление using в классе может использовать имя любого доступного (не закрытого) члена прямого или косвенного базового класса. Доступность имени, указанного в объявлении using, зависит от спецификатора доступа, предшествующего объявлению using. Таким образом, если объявление using расположено в разделе private класса, то имя будет доступно только для членов и друзей. Если объявление находится в разделе public, имя доступно для всех пользователей класса. Если объявление находится в разделе protected, имя доступно только для членов, друзей и производных классов.

Производный класс может предоставить объявление using только для тех имен, доступ к которым разрешен.

Уровни защиты наследования по умолчанию

В разделе 7.2 упоминалось о том, что у классов, определенных с использованием ключевых слов struct, и class разные спецификаторы доступа по умолчанию. Точно так же заданный по умолчанию спецификатор наследования зависит от ключевого слова, используемого при определении производного класса. По умолчанию у производного класса, определенного с ключевым словом class, будет закрытое наследование (private inheritance), а с ключевым словом struct — открытое (public inheritance):

class Base { /* ... */ };

struct D1 : Base { /* ... */ }; // открытое наследование по умолчанию

class D2 : Base { /* ... */ };  // закрытое наследование по умолчанию

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

Для закрытого наследования производный класс должен быть явно определен как private, не следует полагаться на поведение по умолчанию. Это ясно дает понять, что закрытое наследование применено преднамеренно, а не по оплошности.

Упражнения раздела 15.5
Перейти на страницу:

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