friend class Pal; //
};
class Pal {
public:
int f(Base b) { return b.prot_mem; } //
int f2(Sneaky s) { return s.j; } //
//
//
//
int f3(Sneaky s) { return s.prot_mem; } //
};
Факт допустимости функции f3()
может показаться удивительным, но он непосредственно следует из правила, что все классы контролируют доступ к собственным членам. Класс Pal
— друг класса Base
, поэтому класс Pal
может обращаться к членам объектов класса Base
. Это относится и к встроенным в объект класса 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
, не следует полагаться на поведение по умолчанию. Это ясно дает понять, что закрытое наследование применено преднамеренно, а не по оплошности.