Spec-Zone.ru › C++

Спецификаторы доступа

В спецификации члена класса/структуры или объединения определяется доступность последующих членов.

В спецификации базового класса объявления производного класса определяется доступность унаследованных членов последующего базового класса.

Синтаксис

public : объявления членов (1)
protected : объявления членов (2)
private : объявления членов (3)
public базовый класс (4)
protected базовый класс (5)
private базовый класс (6)
1) Члены, объявленные после спецификатора доступа, имеют открытый доступ.
2) Члены, объявленные после спецификатора доступа, имеют защищённый доступ.
3) Члены, объявленные после спецификатора доступа, имеют закрытый доступ.
4) Открытое наследование: публичные и защищённые члены базового класса, перечисленные после спецификатора доступа, сохраняют свой доступ в производном классе.
5) Защищённое наследование: публичные и защищённые члены базового класса, перечисленные после спецификатора доступа, являются защищёнными членами производного класса.
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
}

Защищённый доступ к членам

Защищённые члены образуют интерфейс класса для производных классов (что отличается от публичного интерфейса класса).

Защищённый член класса доступен только

1) членам и друзьям этого класса;
2) членам любого производного класса этого класса, но только тогда, когда класс объекта, через который обращается к защищённому члену, является этим производным классом или производным классом этого производного класса:
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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API