Спецификаторы доступа
В member-specification класса/структуры или объединения задают доступность последующих членов.
В base-specifier объявления производного класса задают доступность унаследованных членов последующего базового класса.
Синтаксис
public : member-declarations | (1) | |
protected : member-declarations | (2) | |
private : member-declarations | (3) | |
public base-class | (4) | |
protected base-class | (5) | |
private base-class | (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, по умолчанию установлен открытый доступ к его членам и базовым классам. Для объединения по умолчанию установлен открытый доступ к его членам.
Чтобы предоставить дополнительным функциям или классам доступ к защищённым или закрытым членам, можно использовать объявление дружественности.
Проверка доступности применяется ко всем именам независимо от их происхождения, поэтому проверяется имя, введённое с помощью typedef или объявления using (за исключением наследуемых конструкторов), а не имя, на которое оно ссылается:
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++23) |
| У типов со стандартным расположением все нестатические члены-данные должны иметь одинаковый уровень доступа. | (начиная с 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
{
friend void h(Base& b, Derived& d);
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 h(Base& b, Derived& d) // Friend of Derived
{
++d.i; // OK: friend of Derived can access a protected
// member through an object of Derived
// ++b.i; // error: friend of Derived is not a friend of Base
}
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 и в функциональном стиле) позволяет преобразовать производное lvalue в ссылку на его закрытый базовый класс или указатель на производный класс в указатель на его закрытый базовый класс.
Наследование
Значение открытого, защищённого и закрытого наследования описано в разделе о производных классах.
Ключевые слова
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.
| DR | Применено к | Опубликованное поведение | Правильное поведение |
|---|---|---|---|
| CWG 1873 | C++98 | защищённые члены были доступны друзьям производных классов | сделаны недоступными |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/access