Управление доступом к членам (C++)

Элементы управления доступом позволяют отделять public интерфейс класса от private сведений о реализации и protected членов, которые используются только производными классами. Спецификатор доступа действует для всех членов, объявленных после него, пока не будет объявлен следующий спецификатор доступа.

class Point
{
public:
    Point( int, int ) // Declare public constructor.;
    Point();// Declare public default constructor.
    int &x( int ); // Declare public accessor.
    int &y( int ); // Declare public accessor.

private:                 // Declare private state variables.
    int _x;
    int _y;

protected:      // Declare protected function for derived classes only.
    Point ToWindowCoords();
};

Доступ по умолчанию находится private в классе и public в структуре или союзе. Спецификаторы доступа класса могут использоваться любое количество раз и в любом порядке. Выделение хранилища для объектов типов классов зависит от реализации. Однако компиляторы должны гарантировать назначение элементов последовательно более высоким адресам памяти между описателями доступа.

Управление доступом к членам

Тип доступа Значение
private Члены класса, объявленные как private могут использоваться только функциями-членами и друзьями (классами или функциями) класса.
protected Члены класса, объявленные как protected могут использоваться функциями-членами и друзьями (классами или функциями) класса. Кроме того, они могут использоваться производными классами данного класса.
public Члены класса, объявленные как public можно использовать любой функцией.

Управление доступом помогает предотвратить использование объектов способами их использования. Эта защита теряется при выполнении явных преобразований типов (приведения).

Примечание.

Управление доступом одинаково применимо ко всем именам: функциям-членам, данным члена, вложенным классам и перечислителям.

Управление доступом в производном классе

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

  • Объявляет ли производный класс базовым классом public с помощью описателя доступа.

  • Какой доступ к членам предоставляется в базовом классе.

В следующей таблице показана взаимосвязь между этими факторами и определен доступ к членам в базовом классе.

Доступ к членам в базовом классе

private protected public
Всегда недоступно с любым доступом к производным данным private в производном классе, если используется private производное private в производном классе, если используется private производное
protected в производном классе, если используется protected производное protected в производном классе, если используется protected производное
protected в производном классе, если используется public производное public в производном классе, если используется public производное

В следующем примере показано наследование доступа:

// access_specifiers_for_base_classes.cpp
class BaseClass
{
public:
    int PublicFunc(); // Declare a public member.
protected:
    int ProtectedFunc(); // Declare a protected member.
private:
    int PrivateFunc(); // Declare a private member.
};

// Declare two classes derived from BaseClass.
class DerivedClass1 : public BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

class DerivedClass2 : private BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

int main()
{
    DerivedClass1 derived_class1;
    DerivedClass2 derived_class2;
    derived_class1.PublicFunc();
    derived_class2.PublicFunc(); // function is inaccessible
}

В DerivedClass1, функция-член PublicFunc является public членом и ProtectedFunc является protected членом, так как BaseClass является базовым классом public . PrivateFuncBaseClassимеет значение private , и недоступно для всех производных классов.

В DerivedClass2функциях PublicFunc и ProtectedFunc считаются private членами, так как BaseClass это базовый private класс. Опять же, это значит, PrivateFunc что privateBaseClassон недоступен для любых производных классов.

Производный класс можно объявить без спецификатора доступа базового класса. В таком случае производный класс считается, private если объявление производного класса использует class ключевое слово. Производные считаются, public если объявление производного класса использует struct ключевое слово. Например, приведенный ниже код

class Derived : Base
...

эквивалентно правилу

class Derived : private Base
...

Аналогично код

struct Derived : Base
...

эквивалентно правилу

struct Derived : public Base
...

Члены, объявленные как private имеющие доступ, недоступны для функций или производных классов, если эти функции или классы не объявлены с помощью friend объявления в базовом классе.

Тип union не может иметь базовый класс.

Примечание.

При указании private базового класса рекомендуется явно использовать private ключевое слово чтобы пользователи производного класса понимали доступ к члену.

Управление доступом и статические члены

При указании базового класса как privateон влияет только на нестатические члены. Открытые статические члены по-прежнему доступны в производных классах. Однако доступ к членам базового класса с помощью указателей, ссылок или объектов может потребовать преобразования, который снова применяет управление доступом. Рассмотрим следующий пример:

// access_control.cpp
class Base
{
public:
    int Print();             // Nonstatic member.
    static int CountOf();    // Static member.
};

// Derived1 declares Base as a private base class.
class Derived1 : private Base
{
};

// Derived2 declares Derived1 as a public base class.
class Derived2 : public Derived1
{
    int ShowCount();    // Nonstatic member.
};

// Define ShowCount function for Derived2.
int Derived2::ShowCount()
{
    // Call static member function CountOf explicitly.
    int cCount = ::Base::CountOf();     // OK.

    // Call static member function CountOf using pointer.
    cCount = this->CountOf();  // C2247: 'Base::CountOf'
                               // not accessible because
                               // 'Derived1' uses 'private'
                               // to inherit from 'Base'
    return cCount;
}

В предыдущем примере кода управление доступом запрещает преобразование указателя на Derived2 к указателю на Base. Указатель this неявно имеет тип Derived2 *. Чтобы выбрать функцию CountOf , this необходимо преобразовать в тип Base *. Такое преобразование не допускается, так как Base является косвенным базовым классомDerived2private. Преобразование в private тип базового класса допустимо только для указателей на непосредственные производные классы. Поэтому указатели типа Derived1 * можно преобразовать в тип Base *.

Явный вызов CountOf функции без использования указателя, ссылки или объекта для его выбора не подразумевает преобразования. Именно поэтому вызов разрешен.

Члены и друзья производного класса T, могут преобразовать указатель в указатель T на прямой базовый private класс T.

Доступ к виртуальным функциям

Управление доступом, применяемое к virtual функциям, определяется типом, используемым для вызова функции. Переопределение объявлений функции не влияет на управление доступом для данного типа. Например:

// access_to_virtual_functions.cpp
class VFuncBase
{
public:
    virtual int GetState() { return _state; }
protected:
    int _state;
};

class VFuncDerived : public VFuncBase
{
private:
    int GetState() { return _state; }
};

int main()
{
   VFuncDerived vfd;             // Object of derived type.
   VFuncBase *pvfb = &vfd;       // Pointer to base type.
   VFuncDerived *pvfd = &vfd;    // Pointer to derived type.
   int State;

   State = pvfb->GetState();     // GetState is public.
   State = pvfd->GetState();     // C2248 error expected; GetState is private;
}

В предыдущем примере вызов виртуальной функции GetState с помощью указателя на вызовы VFuncDerived::GetStateтипов VFuncBase и GetState рассматривается как public. Однако вызов GetState с помощью указателя типа VFuncDerived является нарушением управления доступом, так как GetState объявляется private в классе VFuncDerived.

Внимание

Виртуальную функцию GetState можно вызвать с помощью указателя на базовый класс VFuncBase. Это не означает, что вызываемая функция является базовой версией этой функции.

Управление доступом с множественным наследованием

В решетках множественного наследования, в которых используются виртуальные базовые классы, к конкретным именам можно обращаться несколькими путями. Поскольку в разных путях могут применяться разные средства управления доступом, компилятор выбирает тот путь, который позволяет получить наиболее широкий доступ. См. следующий рисунок:

Diagram showing access along the paths of an inheritance graph.

На схеме показана следующая иерархия наследования: класс VBase является базовым классом. Класс LeftPath наследует от VBase с помощью виртуальной виртуальной private базы данных. Класс RightPath также наследует от VBase, но с помощью виртуальной виртуальной public базы данных. Наконец, класс Производный наследует от класса LeftPath и класса RightPath с помощью public LeftPath, public RightPath.

Доступ вдоль линий графа наследования

На этом рисунке обращение к имени, которое было объявлено в классе VBase, всегда будет выполняться через класс RightPath. Правильный путь является более доступным, так как RightPath объявляется VBase как базовый public класс, а LeftPath объявляется VBase как private.

См. также

Справочник по языку C++