Access specifiers
В member-specification класса class/struct или union определяет доступность последующих членов.
В base-specifier объявления derived class определите доступность унаследованных членов последующего базового класса.
Содержание |
Синтаксис
public
:
объявления-членов
|
(1) | ||||||||
protected
:
объявления-членов
|
(2) | ||||||||
private
:
объявления-членов
|
(3) | ||||||||
| public базовый-класс | (4) | ||||||||
| protected базовый-класс | (5) | ||||||||
| private базовый-класс | (6) | ||||||||
Закрытые члены базового класса всегда недоступны для производного класса независимо от типа наследования: public, protected или private.
Объяснение
Имя каждого class члена (static, non-static, function, type, и т.д.) имеет связанный «уровень доступа к члену». Когда имя члена используется в любом месте программы, его доступ проверяется, и если он не удовлетворяет правилам доступа, программа не компилируется:
#include <iostream> class Example { public: // все объявления после этой точки являются public void add(int x) // член "add" имеет public доступ { n += x; // OK: private Example::n может быть доступен из Example::add } private: // все объявления после этой точки являются private int n = 0; // член "n" имеет private доступ }; int main() { Example e; e.add(1); // OK: public Example::add может быть доступен из main // e.n = 7; // ошибка: private Example::n не может быть доступен из main }
Спецификаторы доступа предоставляют автору класса возможность определять, какие члены класса доступны пользователям класса (то есть интерфейсу ), а какие члены предназначены для внутреннего использования классом ( реализацией ).
Подробно
Все члены класса (тела member functions , инициализаторы объектов-членов и полные определения nested class definitions ) имеют доступ ко всем именам, доступным классу. Локальный класс внутри функции-члена имеет доступ ко всем именам, доступным этой функции-члену.
Класс, определённый с ключевым словом
class
, имеет приватный доступ для своих членов и базовых классов по умолчанию. Класс, определённый с ключевым словом
struct
, имеет публичный доступ для своих членов и базовых классов по умолчанию.
Объединение
имеет публичный доступ для своих членов по умолчанию.
Чтобы предоставить доступ к дополнительным функциям или классам к защищённым или приватным членам, может использоваться объявление дружбы .
Доступность применяется ко всем именам без учета их происхождения, поэтому проверяется имя, введенное с помощью typedef или using declarations (за исключением наследуемых конструкторов), а не имя, на которое оно ссылается:
class A : X { class B {}; // B является приватным в A public: typedef B BB; // BB является публичным }; void f() { A::B y; // ошибка: A::B является приватным A::BB x; // OK: A::BB является публичным }
Доступ к членам не влияет на видимость: имена приватных и унаследованных приватно членов видны и учитываются при разрешении перегрузки, неявные преобразования в недоступные базовые классы всё равно рассматриваются и т.д. Проверка доступа к членам является последним шагом после интерпретации любой данной языковой конструкции. Цель этого правила заключается в том, чтобы замена любого
private
на
public
никогда не изменяла поведение программы.
Проверка доступа для имён, используемых в аргументах по умолчанию функций , а также в стандартных параметрах шаблонов , выполняется в точке объявления, а не в точке использования.
Правила доступа для имён виртуальных функций проверяются в точке вызова с использованием типа выражения, которое обозначает объект, для которого вызывается функция-член. Доступ к финальному переопределению игнорируется:
struct B { virtual int f(); // f является public в B }; class D : public B { private: int f(); // f является private в D }; void f() { D d; B& b = d; b.f(); // OK: B::f является public, вызывается D::f несмотря на private доступ d.f(); // ошибка: D::f является private }
Имя, которое является приватным согласно name lookup без квалификации, может быть доступно через квалифицированный поиск имени:
class A {}; class B : private A {}; class C : public B { A* p; // ошибка: поиск неквалифицированного имени находит A как приватный базовый класс B ::A* q; // OK: поиск квалифицированного имени находит объявление на уровне пространства имен };
Имя, доступное через несколько путей в графе наследования, имеет доступность пути с наибольшим уровнем доступа:
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 доступен для C через B } };
Любое количество спецификаторов доступа может присутствовать внутри класса в любом порядке.
|
Спецификаторы доступа членов могут влиять на расположение класса : адреса нестатических членов-данных гарантированно увеличиваются в порядке объявления только для членов не разделённых спецификатором доступа (до C++11) с одинаковым доступом (начиная с C++11) . |
(до C++23) |
|
Для стандартных типов размещения все нестатические члены данных должны иметь одинаковый уровень доступа. |
(начиная с C++11) |
Когда член переобъявляется в пределах одного и того же класса, это должно происходить с тем же уровнем доступа:
struct S { class A; // S::A является публичным private: class A {}; // ошибка: нельзя изменить уровень доступа };
Доступ к открытым членам
Публичные члены формируют часть публичного интерфейса класса (другими частями публичного интерфейса являются нечленные функции, обнаруживаемые через ADL ).
Открытый член класса доступен везде:
class S { public: // n, E, A, B, C, U, f являются публичными членами int n; enum E {A, B, C}; struct U {}; static void f() {} }; int main() { S::f(); // S::f доступен в main S s; s.n = S::B; // S::n и S::B доступны в main S::U x; // S::U доступен в 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) // функция-член производного класса { ++d.i; // OK: тип d - Derived ++i; // OK: тип неявного '*this' - Derived // ++b.i; // ошибка: нельзя получить доступ к защищённому члену через // Base (иначе можно было бы изменять // другие производные классы, например гипотетический // Derived2, базовую реализацию) } }; void Base::g(Base& b, Derived& d) // функция-член Base { ++i; // OK ++b.i; // OK ++d.i; // OK } void h(Base& b, Derived& d) // Друг Derived { ++d.i; // OK: друг Derived может получить доступ к защищённому // члену через объект типа Derived // ++b.i; // ошибка: друг Derived не является другом Base } void x(Base& b, Derived& d) // не-член, не-друг { // ++b.i; // ошибка: нет доступа от не-члена // ++d.i; // ошибка: нет доступа от не-члена }
Когда формируется указатель на защищённый член, в его объявлении должен использоваться производный класс:
struct Base { protected: int i; }; struct Derived : Base { void f() { // int Base::* ptr = &Base::i; // ошибка: должно использоваться имя через Derived int Base::* ptr = &Derived::i; // OK } };
Доступ к приватным членам
Приватные члены формируют реализацию класса, а также приватный интерфейс для других членов класса.
Приватный член класса доступен только членам и друзьям этого класса, независимо от того, находятся ли члены в одном и том же или разных экземплярах:
class S { private: int n; // S::n является приватным public: S() : n(10) {} // this->n доступен в S::S S(const S& other) : n(other.n) {} // other.n доступен в S::S };
Явное приведение типа (C-стиль и функциональный стиль) позволяет выполнять приведение от производного lvalue к ссылке на его закрытый базовый класс, или от указателя на производный класс к указателю на его закрытый базовый класс.
Наследование
См. производные классы для понимания значения public, protected и private наследования.
Ключевые слова
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 1873 | C++98 | protected members were accessible to friends of derived classes | made inaccessible |