Спецификаторы доступа
В спецификации члена класса/структуры или объединения определяется доступность последующих членов.
В спецификации базового класса объявления производного класса определяется доступность унаследованных членов последующего базового класса.
Синтаксис
public : объявления членов |
(1) | |
protected : объявления членов |
(2) | |
private : объявления членов |
(3) | |
public базовый класс |
(4) | |
protected базовый класс |
(5) | |
private базовый класс |
(6) |
Закрытые члены базового класса всегда недоступны для производного класса, независимо от того, является ли наследование открытым, защищенным или закрытым.
Объяснение
У каждого члена класса (статического, нестатического, функции, типа и т. д.) есть связанный с ним "доступ к члену". При использовании имени члена в программе проверяется доступ, и если он не соответствует правилам доступа, программа не компилируется:
#include <iostream>
class Example
{
public: // all declarations after this point are public
void add(int x) // member "add" has public access
{
n += x; // OK: private Example::n can be accessed from Example::add
}
private: // all declarations after this point are private
int n = 0; // member "n" has private access
};
int main()
{
Example e;
e.add(1); // OK: public Example::add can be accessed from main
// e.n = 7; // error: private Example::n cannot be accessed from main
}Спецификаторы доступа позволяют автору класса определять, какие члены класса доступны пользователям класса (т. е. интерфейс) и какие члены предназначены для внутреннего использования класса (реализация).
Подробности
Все члены класса (тела членов-функций, инициализаторы членов-объектов и все вложенные определения классов) имеют доступ ко всем именам, к которым может получить доступ класс. Вложенный класс внутри члена-функции имеет доступ ко всем именам, к которым может получить доступ член-функция.
Класс, определённый с ключевым словом class, по умолчанию имеет закрытый доступ к своим членам и базовым классам. Класс, определённый с ключевым словом struct, по умолчанию имеет открытый доступ к своим членам и базовым классам. Объединение (union) по умолчанию имеет открытый доступ к своим членам.
Для предоставления доступа к дополнительным функциям или классам к защищённым или закрытым членам можно использовать объявление дружбы.
Доступность относится ко всем именам независимо от их происхождения, поэтому имя, введённое с помощью typedef или using declarations (за исключением наследуемых конструкторов), проверяется, а не имя, к которому оно относится:
class A : X
{
class B {}; // B is private in A
public:
typedef B BB; // BB is public
};
void f()
{
A::B y; // error: A::B is private
A::BB x; // OK: A::BB is public
}Доступ к членам не влияет на видимость: имена закрытых и закрыто унаследованных членов видны и учитываются при разрешении перегрузки, неявные преобразования к недоступным базовым классам всё ещё учитываются и т. д. Проверка доступа к членам – это последний этап после интерпретации любого заданного языкового конструкта. Цель этого правила заключается в том, что замена любого private на public никогда не изменяет поведение программы.
Проверка доступа к именам, используемым в параметрах по умолчанию функций, а также в параметрах по умолчанию шаблонов, выполняется на момент объявления, а не на момент использования.
Правила доступа к именам виртуальных функций проверяются в точке вызова с использованием типа выражения, используемого для обозначения объекта, для которого вызывается член-функция. Доступ к окончательному переопределителю игнорируется:
struct B
{
virtual int f(); // f is public in B
};
class D : public B
{
private:
int f(); // f is private in D
};
void f()
{
D d;
B& b = d;
b.f(); // OK: B::f is public, D::f is invoked even though it's private
d.f(); // error: D::f is private
}Имя, которое является закрытым в соответствии с неквалифицированным поиском имён, может быть доступно через квалифицированный поиск имён:
class A {};
class B : private A {};
class C : public B
{
A* p; // error: unqualified name lookup finds A as the private base of B
::A* q; // OK: qualified name lookup finds the namespace-level declaration
};Имя, доступное по нескольким путям в графе наследования, имеет доступность пути с максимальным доступом:
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(); // OK: W is accessible to C through B
}
};В классе может быть любое количество спецификаторов доступа в любом порядке. Спецификаторы доступа к членам могут влиять на расположение класса: адреса нестатических членов данных гарантируются только для увеличения в порядке объявления для членов не разделённых спецификатором доступа(до C++11)с тем же доступом(с C++11).
Для типов стандартного расположения все нестатические члены данных должны иметь одинаковый доступ. |
(с C++11) |
Когда член переопределяется в том же классе, он должен делать это с тем же доступом к члену:
struct S
{
class A; // S::A is public
private:
class A {}; // error: cannot change access
};Открытый доступ к членам
Публичные члены составляют часть публичного интерфейса класса (другие части публичного интерфейса – это функции, не являющиеся членами, которые находятся с помощью ADL).
Публичный член класса доступен в любом месте:
class S
{
public:
// n, E, A, B, C, U, f are public members
int n;
enum E {A, B, C};
struct U {};
static void f() {}
};
int main()
{
S::f(); // S::f is accessible in main
S s;
s.n = S::B; // S::n and S::B are accessible in main
S::U x; // S::U is accessible in main
}Защищённый доступ к членам
Защищённые члены образуют интерфейс класса для производных классов (что отличается от публичного интерфейса класса).
Защищённый член класса доступен только
struct Base
{
protected:
int i;
private:
void g(Base& b, struct Derived& d);
};
struct Derived : Base
{
void f(Base& b, Derived& d) // member function of a derived class
{
++d.i; // OK: the type of d is Derived
++i; // OK: the type of the implied '*this' is Derived
// ++b.i; // error: can't access a protected member through
// Base (otherwise it would be possible to change
// other derived classes, like a hypothetical
// Derived2, base implementation)
}
};
void Base::g(Base& b, Derived& d) // member function of Base
{
++i; // OK
++b.i; // OK
++d.i; // OK
}
void x(Base& b, Derived& d) // non-member non-friend
{
// ++b.i; // error: no access from non-member
// ++d.i; // error: no access from non-member
}При формировании указателя на защищённый член необходимо использовать производный класс в его объявлении:
struct Base
{
protected:
int i;
};
struct Derived : Base
{
void f()
{
// int Base::* ptr = &Base::i; // error: must name using Derived
int Base::* ptr = &Derived::i; // OK
}
};Закрытый доступ к членам
Закрытые члены формируют реализацию класса, а также закрытый интерфейс для других членов класса.
Закрытый член класса доступен только членам и друзьям этого класса, независимо от того, находятся ли члены на тех же или разных экземплярах:
class S
{
private:
int n; // S::n is private
public:
S() : n(10) {} // this->n is accessible in S::S
S(const S& other) : n(other.n) {} // other.n is accessible in S::S
};Явное приведение (C-стиль и стиль функций) позволяет приводить значение производного объекта слева от знака = к ссылке на его закрытый базовый класс или указатель на производный к указателю на его закрытый базовый класс.
Наследование
См. производные классы для понимания значения открытого, защищённого и закрытого наследования.
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применено к | Поведение как опубликовано | Правильное поведение |
|---|---|---|---|
| CWG 1873 | C++98 | защищённые члены были доступны для друзей производных классов | сделаны недоступными |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/access